#include "slice.hpp"

#include "qbe/src/ecs/app.hpp"

#include "qbe/src/ecs/query.hpp"

#include "qbe/src/axis/axis.hpp"
#include "qbe/src/input/input.hpp"
#include "qbe/src/select/select.hpp"
#include "qbe/src/state/state.hpp"
#include "qbe/src/undoRedo/undoRedo.hpp"
#include "qbe/src/utils/utils.hpp"

#include "qbe/src/layout/layout.hpp"

#include "qlib/all.h"

namespace qbe::slice {

// 退出模式时回调
void exit_callback(ecs::Query query, state::AllModeType mode) {
  auto& move_param = query.get_resource<Models>();
  auto& layout_cmd = query.get_resource<layout::LayoutCmd>();
  auto& state = query.get_resource<state::State>();

  auto& cell = query.get_resource<qlib::Cell::s_ptr>();
  auto& reg = cell->reg;

  move_param.reset();
}

void init(ecs::Commands commands, ecs::Query query) {
  Models models;
  commands.insert_resource(models);

  auto& state = query.get_resource<state::State>();
  state.addExitCall(state::EditingModeType::Slice, exit_callback);
}

// 退出模式
void exit(ecs::Query query) {
  // 获取移动参数
  auto& move_param = query.get_resource<Models>();
  auto& layout_cmd = query.get_resource<layout::LayoutCmd>();

  // 获取reg
  auto& cell = query.get_resource<qlib::Cell::s_ptr>();
  auto& reg = cell->reg;

  // 退出模式
  auto& state = query.get_resource<state::State>();
  state.exit();
  // state.switchMode(state::SelectModeType::Overall);

  move_param.reset();
}

// 获取鼠标当前点，需要考虑：吸附、斜角吸附
qlib::Vec2d getNowPoint(ecs::Query query) {
  auto& state = query.get_resource<state::State>();

  if (!state.isAdsorption()) {
    auto& axisModule = query.get_resource<axis::Axis>();
    qlib::Vec2d pos = {axisModule.real_x, axisModule.real_y};
    return pos;
  } else {
    return utils::getAdsorbedPoint(query);
  }
}

// 帧开始，模式转换
void stateTransformBegin(Models& models, state::State& state, input::HandleBundle* handle, ecs::Bus& bus, ecs::Query query) {
  if (models.state == State::None) {
    models.state = State::Wait;
  }

  if (models.state == State::Wait && handle->mousePressed(vsg::BUTTON_MASK_1)) {
    models.state = State::Complete;
    models.pos = getNowPoint(query);
  }

  if (models.state == State::Wait && handle->keyReleased(vsg::KEY_Escape)) {
    models.state = State::Cancel;
  }

  if (models.state == State::Wait && handle->keyPressed(vsg::KEY_Space)) {
    models.isXSlice = !models.isXSlice;
  }
}

// 帧尾-退出模式
void stateTransformEnd(State& state) {}

template <typename T, typename... Args>
void slice_impl(entt::registry& reg, entt::entity e, undoRedoT::UndoRedoModule& undoRedoModule, layout::LayoutCmd& cmd, Models& models,
                const std::vector<double>& positions, bool x_axis, double scaling, qlib::Vec2 pos) {
  auto ptr = reg.try_get<std::shared_ptr<T>>(e);

  auto default_slice = [&]() {
    qlib::PolygonPtrArray polygons;
    if constexpr (std::is_same<T, qlib::Polygon>::value) {
      polygons.push_back(*ptr);
    } else if constexpr (std::is_same<T, qlib::Path>::value) {
      if ((*ptr)->width == 0) {
        auto poly = qlib::Polygon::create();
        poly->layer = (*ptr)->layer;
        poly->points = (*ptr)->spine.points;
        polygons.push_back(poly);
      } else {
        (*ptr)->to_polygons(false, {0, 0}, polygons);
      }
    } else if constexpr (std::is_same<T, qlib::Label>::value) {
      auto polys_t = (*ptr)->to_polygons();
      polygons.insert(polygons.begin(), polys_t.begin(), polys_t.end());
    } else if constexpr (std::is_same<T, qlib::Text>::value) {
      auto polys_t = (*ptr)->to_polygons();
      polygons.insert(polygons.begin(), polys_t.begin(), polys_t.end());
    } else {
      qlib::Polygon::s_ptr polygon = (*ptr)->to_polygon();
      polygon->layer = (*ptr)->layer;
      polygons.push_back(polygon);
    }

    std::vector<qlib::PolygonPtrArray> res;
    for (auto poly : polygons) {
      std::vector<qlib::PolygonPtrArray> res_t;
      qlib::ClipperTool::slice(poly, positions, x_axis, scaling, res_t);
      for (auto res_tt : res_t) {
        for (auto res_ttt : res_tt) {
          res_ttt->layer = poly->layer;
        }
      }

      res.insert(res.begin(), res_t.begin(), res_t.end());
    }

    if (!res.empty()) {
      undoRedoModule.dele(e);
      cmd.remove(e);

      for (auto poly : res) {
        auto es = cmd.add(poly);
        undoRedoModule.add(es);
        for (auto et : es) {
          reg.emplace_or_replace<select::SelectTag>(et);
        }
      }
    }
  };

  if (ptr != nullptr) {
    if constexpr (std::is_same_v<T, qlib::Path>) {
      qlib::PathPtrArray res;
      qlib::ClipperTool::slice(*ptr, pos.x, pos.y, x_axis, res);

      if (!res.empty()) {
        undoRedoModule.dele(e);
        cmd.remove(e);

        for (auto poly : res) {
          auto e = cmd.add(poly);
          undoRedoModule.add(e);
          reg.emplace_or_replace<select::SelectTag>(e);
        }
      }
    } else {
      default_slice();
    }

    return;
  }

  if constexpr (sizeof...(Args) > 0) {
    return slice_impl<Args...>(reg, e, undoRedoModule, cmd, models, positions, x_axis, scaling, pos);
  } else {
    // assert(0);
  }
}

#define QLIB_SLICE_ELEMENT_TYPE qlib::Polygon, qlib::Path, qlib::Rectangle, qlib::Label, qlib::Ellipse, qlib::Fan

void sliceObj(entt::registry& reg, entt::entity e, undoRedoT::UndoRedoModule& undoRedoModule, layout::LayoutCmd& cmd, Models& models,
              const std::vector<double>& positions, bool x_axis, double scaling, Vec2d pos) {
  slice_impl<QLIB_SLICE_ELEMENT_TYPE>(reg, e, undoRedoModule, cmd, models, positions, x_axis, scaling, pos);
}

// 移动逻辑
void update(ecs::Query query, ecs::Bus& bus) {
  // 判断编辑模式为移动
  auto& state = query.get_resource<state::State>();

  if (state.getEditingMode() != state::EditingModeType::Slice) {
    return;
  }

  // 获取cell和reg
  auto& cell = query.get_resource<qlib::Cell::s_ptr>();
  auto& reg = cell->reg;
  auto& layout_cmd = query.get_resource<layout::LayoutCmd>();

  // 获取输入 坐标系 撤销恢复
  auto& handModule = query.get_resource<vsg::ref_ptr<input::HandleBundle>>();
  auto& axisModule = query.get_resource<axis::Axis>();
  auto& undoRedoModule = query.get_resource<undoRedoT::UndoRedoModule>();

  // 获取移动参数
  auto& models = query.get_resource<Models>();

  if (handModule->keyReleased(vsg::KEY_Space)) {
    models.isXSlice = !models.isXSlice;
  }

  stateTransformBegin(models, state, handModule, bus, query);

  if (models.state == State::Cancel) {
    models.reset();
    state.exit();
    state.switchMode(state::SelectModeType::Overall);
    return;
  }

  if (models.state == State::Complete) {
    auto view = reg.view<select::SelectTag>();
    std::vector<double> positions;
    bool x_axis;
    double scaling;

    x_axis = !models.isXSlice;
    scaling = 1.0;

    positions.resize(1);
    if (x_axis) {
      positions[0] = models.pos.x;
    } else {
      positions[0] = models.pos.y;
    }

    undoRedoModule.start(cell);

    view.each([&reg, &undoRedoModule, &layout_cmd, &models, &positions, &x_axis, &scaling](entt::entity e) {
      sliceObj(reg, e, undoRedoModule, layout_cmd, models, positions, x_axis, scaling);
    });

    undoRedoModule.end();

    auto& sele_event_writer = query.get_event_writer<select::Event>();
    sele_event_writer.send({select::Code::SelectUpdate});
    sele_event_writer.send({select::Code::HoverUpdate});

    models.reset();
    state.exit();
    state.switchMode(state::SelectModeType::Overall);
  }

  stateTransformEnd(models.state);
}

void Plugin::build(ecs::App& app) {
  app.add_stage<InitStage>();
  app.add_stage<UpdateStage>();

  app.add_systems<InitStage>(init);
  app.add_systems<UpdateStage>(update);
}

}  // namespace qbe::slice